﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Matrix
{
/*    public abstract class Matrix
    {

        public abstract void WriteMatr();

        public abstract double GetElem(int nRow, int nCol);
        public abstract void Transpose();
        public abstract Matrix Add(Matrix M1);
        public abstract Matrix Subtract(Matrix M1);
        public abstract Matrix Mul(Matrix M1);
    }*/
    public class GeneralMatrix
    {
        protected int size = 0;
        protected int hash = 10; 
        private double[,] elems = null;

        public GeneralMatrix()
        {
        }
        public GeneralMatrix(int n, double[,] elems)
        {
            ReadMatr(n, elems);
        }
        public void ReadMatr(int n, double[,] elems)
        {
            this.elems = new double[n, n];
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    this.elems[i, j] = elems[i, j];
            size = n;
        }
        public virtual void WriteMatr()
        {
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < size; j++)
                    Console.Write(this.GetElem(i, j) + "\t");
                Console.WriteLine();
            }
        }
        public int GetSize()
        {
            return size;
        }
        public override int GetHashCode()
        {
            return hash;
        }
        public virtual double GetElem(int nRow, int nCol)
        {
            return elems[nRow, nCol];
        }
        public virtual void SetElem(int nRow, int nCol, double val)
        {
            elems[nRow, nCol] = val;
        }
        public virtual void Transpose()
        {
            double[,] newelems = new double[size, size];
            for (int i = 0; i < size; i++)
                for (int j = 0; j < size; j++)
                    newelems[j, i] = elems[i, j];
            elems = newelems;            
        }
        public virtual GeneralMatrix Add(GeneralMatrix M1)
        {
            if (this.size != M1.GetSize())
            {
                return null;
            }
            if(M1.GetHashCode() <= this.GetHashCode())
            {
                GeneralMatrix Mres = new GeneralMatrix(size, elems);
                for (int i = 0; i < size; i++)
                    for (int j = 0; j < size; j++)
                        Mres.elems[i, j] += M1.GetElem(i, j);
                return Mres;
            }
            else
            {
                return M1.Add(this);
            }
        }
        public virtual GeneralMatrix Subtract(GeneralMatrix M1)
        {
            if (this.size != M1.GetSize())
            {
                return null;
            }
            if(M1.GetHashCode() <= this.GetHashCode())
            {
                GeneralMatrix Mres = new GeneralMatrix(size, elems);
                for (int i = 0; i < size; i++)
                    for (int j = 0; j < size; j++)
                        Mres.elems[i, j] -= M1.GetElem(i, j); 
                return Mres;
            }
            else
            {
                return M1.Subtract(this);
            }
        }
        public virtual GeneralMatrix Mul(GeneralMatrix M1)
        {
            if (size != M1.GetSize())
            {
                return null;
            }
            double[,] nullarray = new double[size, size];
            for (int i = 0; i < size; i++)
                for (int j = 0; j < size; j++)
                    nullarray[i, j] = 0;
            GeneralMatrix Mres = new GeneralMatrix(size, nullarray);
            for (int i = 0; i < size; i++)
                for (int j = 0; j < size; j++)
                {
                    for (int k = 0; k < size; k++)
                        Mres.elems[i, j] += this.GetElem(i, k) * M1.GetElem(k, j);
                }
            return Mres;
        }
        /*     public static GeneralMatrix operator +(GeneralMatrix M1, GeneralMatrix M2)
             {
                 if(M1.nCols != M2.nCols || M1.nRows != M2.nRows)
                     return null;
                 GeneralMatrix Mres = new GeneralMatrix(M1.nCols, M1.elems);
                 for (int i = 0; i < M1.nRows; i++)
                     for (int j = 1; j < M1.nCols; j++)
                         Mres.elems[i, j] += M2.elems[i,j];                
                 return Mres;
             }
             public static GeneralMatrix operator -(GeneralMatrix M1, GeneralMatrix M2)
             {
                 if(M1.nCols != M2.nCols || M1.nRows != M2.nRows)
                     return null;
                 GeneralMatrix Mres = new GeneralMatrix(M2.nCols, M1.elems);
                 for (int i = 0; i < M1.nRows; i++)
                     for (int j = 1; j < M1.nCols; j++)
                         Mres.elems[i, j] -= M2.elems[i,j];                
                 return Mres;
             }
             public static GeneralMatrix operator *(GeneralMatrix M1, GeneralMatrix M2)
             {
                 if (M2.nRows != M1.nCols)
                     return null;
                 double[,] nullarray = new double[M1.nRows, M2.nCols];
                 for (int i = 0; i < M1.nRows; i++)
                     for (int j = 0; j < M2.nCols; j++)
                         nullarray[i, j] = 0;
                 GeneralMatrix Mres = new GeneralMatrix(M2.nCols, nullarray);
                 for (int i = 0; i < M1.nRows; i++)
                     for (int j = 0; j < M2.nCols; j++)
                     {
                         for (int k = 0; k < M2.nCols; k++)
                             Mres.elems[i, j] += M1.elems[i, k] * M2.elems[k, j];
                     }
                 return Mres;
             }*/
    }


    public class UpperTriangleMatrix : GeneralMatrix
    {
        double[][] elems = null; 

        public UpperTriangleMatrix()
        {}
        public UpperTriangleMatrix(int n, double[][] elems)
        {
            ReadMatr(n, elems);
        }

        public void ReadMatr(int n, double[][] elems)
        {
            this.elems = (double[][])elems.Clone();
            /*this.elems = new double[n][];
            for (int i = 0; i < n; i++)
            {
                this.elems[i] = new double[n - i];
                for (int j = i; j < n; j++)
                {
                    this.elems[i][j] = elems[i][j];
                }
            }*/
            size = n;
        }
        public override int GetHashCode()
        {
            return base.GetHashCode() - 1;
        }
        public override double GetElem(int nRow, int nCol)
        {
            return (nCol >= nRow) ? elems[nRow][nCol - nRow] : 0;
        }
        public override void SetElem(int nRow, int nCol, double val)
        {
            if (nCol >= nRow)
            {
                elems[nRow][nCol - nRow] = val;
            }
        }
        
        public override void Transpose()
        {
            double elem = 0;
            for (int i = 0; i < size; i++)
                for (int j = i; j < size; j++)
                {
                    elem = GetElem(i,j);
                    SetElem(i,j,GetElem(j,i));
                    //elems[i][j] = elems[j][i];
                    //elems[j][i] = elem;
                    SetElem(j, i, elem);
                }
        }
        public override GeneralMatrix Add(GeneralMatrix M1)
        {
            if (this.size != M1.GetSize())
            {
                return null;
            }
            if(M1.GetHashCode() <= this.GetHashCode())
            {
                UpperTriangleMatrix Mres = new UpperTriangleMatrix(size, elems);
                for (int i = 0; i < size; i++)
                {
                    for (int j = i; j < size; j++)
                    {
                        Mres.SetElem(i, j, M1.GetElem(i, j) + Mres.GetElem(i, j));
                        //Mres.elems[i][j] += M1.GetElem(i, j);
                    }
                }
                return Mres;
            }
            else
            {
                return M1.Add(this);
            }
        }
        public override GeneralMatrix Subtract(GeneralMatrix M1)
        {
            if (this.size != M1.GetSize())
            {
                return null;
            }
            if(M1.GetHashCode() <= this.GetHashCode())
            {
                UpperTriangleMatrix Mres = new UpperTriangleMatrix(size, elems);
                for (int i = 0; i < size; i++)
                {
                    for (int j = i; j < size; j++)
                    {
                        Mres.SetElem(i, j, Mres.GetElem(i, j) - M1.GetElem(i, j));
                        //Mres.elems[i][j] += M1.GetElem(i, j);
                    }
                }
                return Mres;
            }
            else
            {
                return M1.Subtract(this);
            }
        }
        public override GeneralMatrix Mul(GeneralMatrix M1)
        {
            if (size != M1.GetSize())
            {
                return null;
            }
            double[][] nullarray = (double[][])elems.Clone();
            for (int i = 0; i < size; i++)
                for (int j = 0; j < size - i; j++)
                    nullarray[i][j] = 0;
            if (M1.GetHashCode() <= this.GetHashCode())
            {
                UpperTriangleMatrix Mres = new UpperTriangleMatrix(size, nullarray);
                for (int i = 0; i < size; i++)
                {
                    for (int j = i; j < size; j++)
                    {
                        for (int k = 0; k < size; k++)
                        {
                            Mres.SetElem(i, j, Mres.GetElem(i, j) + GetElem(i, k) * M1.GetElem(k, j));
                            //Mres.elems[i][j] += this.GetElem(i, k) * M1.GetElem(k, j);
                        }
                    }
                }
                return Mres;
            }
            else
            {
                return M1.Mul(this);
            }
            
        }
        /*public static UpperTriangleMatrix operator +(UpperTriangleMatrix M1, UpperTriangleMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            UpperTriangleMatrix Mres = new UpperTriangleMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 1; j < M1.nRows; j++)
                    Mres.elems[i, j] += M2.elems[i, j];
            return Mres;
        }
        public static UpperTriangleMatrix operator -(UpperTriangleMatrix M1, UpperTriangleMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            UpperTriangleMatrix Mres = new UpperTriangleMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 1; j < M1.nRows; j++)
                    Mres.elems[i, j] -= M2.elems[i, j];
            return Mres;
        }
        public static UpperTriangleMatrix operator *(UpperTriangleMatrix M1, UpperTriangleMatrix M2)
        {
            if (M2.nRows != M1.nRows)
                return null;
            double[,] nullarray = new double[M1.nRows, M2.nRows];
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 0; j < M2.nRows; j++)
                    nullarray[i, j] = 0;
            UpperTriangleMatrix Mres = new UpperTriangleMatrix(M1.nRows, nullarray);
            for (int i = 0; i < M1.nRows; i++)
                for (int j = i; j < M1.nRows; j++)
                {
                    for (int k = 0; k < M1.nRows; k++)
                        Mres.elems[i, j] += M1.elems[i, k] * M2.elems[k, j];
                }
            return Mres;
        }*/
    }
    
    public class DiagonalMatrix : UpperTriangleMatrix
    {
        double[] elems = null;
        public DiagonalMatrix()
        {}
        public DiagonalMatrix(int n, double[] elems)
        {
            ReadMatr(n, elems);
        }
        public void ReadMatr(int n, double[] elems)
        {
            elems = new double[n];
            for (int i = 0; i < n; i++)
                    elems[i] = elems[i];
            size = n;
        }
        public override int GetHashCode()
        {
            return base.GetHashCode() - 1;
        }
        public override double GetElem(int nRow, int nCol)
        {
            return (nCol == nRow) ? elems[nRow] : 0;
        }
        public override void SetElem(int nRow, int nCol, double val)
        {
            if (nCol == nRow)
            {
                elems[nRow] = val;
            }
        }
        public override void Transpose()
        {
        }
        public override GeneralMatrix Add(GeneralMatrix M1)
        {
            if (this.size != M1.GetSize())
            {
                return null;
            }
            if (M1.GetHashCode() <= this.GetHashCode())
            {
                DiagonalMatrix Mres = new DiagonalMatrix(size, elems);
                for (int i = 0; i < size; i++)
                {
                    Mres.SetElem(i, i, M1.GetElem(i, i) + Mres.GetElem(i, i));
                }
                return Mres;
            }
            else
            {
                return M1.Add(this);
            }
        }
        public override GeneralMatrix Subtract(GeneralMatrix M1)
        {
            if (this.size != M1.GetSize())
            {
                return null;
            }
            if (M1.GetHashCode() <= this.GetHashCode())
            {
                DiagonalMatrix Mres = new DiagonalMatrix(size, elems);
                for (int i = 0; i < size; i++)
                {
                    Mres.SetElem(i, i, Mres.GetElem(i, i) - M1.GetElem(i, i));
                }
                return Mres;
            }
            else
            {
                return M1.Subtract(this);
            }
        }
        public override GeneralMatrix Mul(GeneralMatrix M1)
        {
            if (size != M1.GetSize())
            {
                return null;
            }
            double[]nullarray = (double[])elems.Clone();
            for (int i = 0; i < size; i++)
                    nullarray[i]= 0;
            if (M1.GetHashCode() <= this.GetHashCode())
            {
                DiagonalMatrix Mres = new DiagonalMatrix(size, nullarray);
                for (int i = 0; i < size; i++)
                {
                    Mres.SetElem(i, i, GetElem(i, i) * M1.GetElem(i, i));
                }
                return Mres;
            }
            else 
            {
                return M1.Mul(this);
            }
            
        }
       /* public static DiagonalMatrix operator +(DiagonalMatrix M1, DiagonalMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            DiagonalMatrix Mres = new DiagonalMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                    Mres.elems[i, i] += M2.elems[i, i];
            return Mres;
        }
        public static DiagonalMatrix operator -(DiagonalMatrix M1, DiagonalMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            DiagonalMatrix Mres = new DiagonalMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                Mres.elems[i, i] -= M2.elems[i, i];
            return Mres;
        }
        public static DiagonalMatrix operator *(DiagonalMatrix M1, DiagonalMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            DiagonalMatrix Mres = new DiagonalMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                Mres.elems[i, i] *= M2.elems[i, i];
            return Mres;
        }*/
    }
    public class ScalarMatrix : DiagonalMatrix
    {
        double elem = 0;

        public ScalarMatrix()
        {
        }
        public ScalarMatrix(int n, double elem)
        {
            ReadMatr(n, elem);
        }
        public void ReadMatr(int n, double elem)
        {
            this.elem = elem;
            size = n;
        }
        public override int GetHashCode()
        {
            return base.GetHashCode() - 1;
        }
        public override double GetElem(int nRow, int nCol)
        {
            return (nCol == nRow) ? elem : 0;
        }
        public override void SetElem(int nRow, int nCol, double val)
        {
            if (nCol == nRow)
            {
                elem = val;
            }
        }
        public override void Transpose()
        {
        }
        public override GeneralMatrix Add(GeneralMatrix M1)
        {
            if (this.size != M1.GetSize())
            {
                return null;
            }
            if (M1.GetHashCode() <= this.GetHashCode())
            {
                ScalarMatrix Mres = new ScalarMatrix(size, elem);
                Mres.SetElem(0, 0, M1.GetElem(0, 0) + Mres.GetElem(0, 0));
                return Mres;
            }
            else
            {
                return M1.Add(this);
            }
        }
        public override GeneralMatrix Subtract(GeneralMatrix M1)
        {
            if (this.size != M1.GetSize())
            {
                return null;
            }
            if (M1.GetHashCode() <= this.GetHashCode())
            {
                ScalarMatrix Mres = new ScalarMatrix(size, elem);
                Mres.SetElem(0, 0, Mres.GetElem(0, 0) - M1.GetElem(0, 0));
                return Mres;
            }
            else
            {
                return M1.Subtract(this);
            }
        }
        public override GeneralMatrix Mul(GeneralMatrix M1)
        {
            if (size != M1.GetSize())
            {
                return null;
            }
            double nullarray = 0;
            if (M1.GetHashCode() <= this.GetHashCode())
            {
                ScalarMatrix Mres = new ScalarMatrix(size, nullarray);
                Mres.SetElem(0, 0, GetElem(0, 0) * M1.GetElem(0, 0));
                return Mres;
            }
            else
            {
                return M1.Mul(this);
            }

        }
       /* public static ScalarMatrix operator +(ScalarMatrix M1, ScalarMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            ScalarMatrix Mres = new ScalarMatrix(M1.nRows, M1.elems);
            double M2elem = M2.elems[0, 0];
            for (int i = 0; i < M1.nRows; i++)
                    Mres.elems[i, i] += M2elem;
            return Mres;
        }
        public static ScalarMatrix operator -(ScalarMatrix M1, ScalarMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            ScalarMatrix Mres = new ScalarMatrix(M1.nRows, M1.elems);
            double M2elem = M2.elems[0, 0];
            for (int i = 0; i < M1.nRows; i++)
                Mres.elems[i, i] -= M2elem;
            return Mres;
        }
        public static ScalarMatrix operator *(ScalarMatrix M1, ScalarMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            ScalarMatrix Mres = new ScalarMatrix(M1.nRows, M1.elems);
            double M2elem = M2.elems[0, 0];
            for (int i = 0; i < M1.nRows; i++)
                Mres.elems[i, i] *= M2elem;
            return Mres;
        }*/
    }

}
    